package es.cv.gui;

import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

import org.apache.commons.math3.ml.clustering.Cluster;
import org.apache.commons.math3.ml.clustering.DBSCANClusterer;
import org.apache.commons.math3.ml.clustering.DoublePoint;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.MatOfByte;
import org.opencv.core.MatOfPoint;
import org.opencv.core.Point;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.highgui.Highgui;
import org.opencv.highgui.VideoCapture;
import org.opencv.imgproc.Imgproc;
import org.opencv.video.BackgroundSubtractorMOG;
import org.opencv.video.BackgroundSubtractorMOG2;

import es.cv.core.CoreModule;

public class BackgroundSubtraction_ClusteringCentroide extends JPanel implements MouseListener, Runnable {
	private static final long serialVersionUID = 1L;
	private BufferedImage image;

	public BackgroundSubtraction_ClusteringCentroide() {
		super();
	}

	private BufferedImage getimage() {
		return image;
	}

	private void setimage(BufferedImage newimage) {
		image = newimage;
		return;
	}

	public static BufferedImage matToBufferedImage(Mat matrix) {
		int cols = matrix.cols();
		int rows = matrix.rows();
		int elemSize = (int) matrix.elemSize();
		byte[] data = new byte[cols * rows * elemSize];
		int type;
		matrix.get(0, 0, data);
		switch (matrix.channels()) {
		case 1:
			type = BufferedImage.TYPE_BYTE_GRAY;
			break;
		case 3:
			type = BufferedImage.TYPE_3BYTE_BGR;
			// bgr to rgb
			byte b;
			for (int i = 0; i < data.length; i = i + 3) {
				b = data[i];
				data[i] = data[i + 2];
				data[i + 2] = b;
			}
			break;
		default:
			return null;
		}
		BufferedImage image2 = new BufferedImage(cols, rows, type);
		image2.getRaster().setDataElements(0, 0, cols, rows, data);
		return image2;
	}

	public void paintComponent(Graphics g) {
		try {
			BufferedImage temp = getimage();
			g.drawImage(temp, 10, 10, temp.getWidth(), temp.getHeight(), this);
		} catch (Exception e) {

		}
	}

	public static void showResult(Mat img) {
		Imgproc.resize(img, img, new Size(img.size().width, img.size().height));
		MatOfByte matOfByte = new MatOfByte();
		Highgui.imencode(".jpg", img, matOfByte);
		byte[] byteArray = matOfByte.toArray();
		BufferedImage bufImage = null;
		try {
			InputStream in = new ByteArrayInputStream(byteArray);
			bufImage = ImageIO.read(in);
			JFrame frame = new JFrame();
			frame.getContentPane().add(new JLabel(new ImageIcon(bufImage)));
			frame.pack();
			frame.setVisible(true);
		} catch (Exception e) {
		}
	}

	private static boolean inicio;

	public static void main(String[] args) {
		// Load the native library.
		System.loadLibrary(Core.NATIVE_LIBRARY_NAME);

		JFrame frame = new JFrame("Background Subtraction + SURF");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setSize(900, 600);
		BackgroundSubtraction_ClusteringCentroide panel = new BackgroundSubtraction_ClusteringCentroide();
		frame.setContentPane(panel);
		frame.setVisible(true);

		panel.addMouseListener(panel);

		BackgroundSubtractorMOG bgMOG = new BackgroundSubtractorMOG();
		Mat mask = new Mat();

		Mat webcam_image = new Mat();
		BufferedImage temp;
		VideoCapture capture = new VideoCapture(1);
		//VideoCapture capture = new VideoCapture(0);

		if (capture.isOpened()) {
			inicio = false;
			while (!inicio) {
				capture.read(webcam_image);
				if (!webcam_image.empty()) {
					frame.setSize(webcam_image.width() + 40, webcam_image.height() + 60);
					temp = matToBufferedImage(webcam_image);
					panel.setimage(temp);
					panel.repaint();
				} else {
					break;
				}
			}
			ArrayList<DoublePoint> currentCentroidsFromClusters = new ArrayList<DoublePoint>();
			int framesCounter = 0;
			int maximumFramesToBeChecked = 30;
			int dbscanRadius = 5;
			int dbscanMinCentroids = maximumFramesToBeChecked / 3;
			int dbScanPerFrameRadius = 15;
			int dbScanPerFrameMinPoints = 30;
			while (true) {
				capture.read(webcam_image);
				if (!webcam_image.empty()) {
					frame.setSize(webcam_image.width() + 40, webcam_image.height() + 60);

					bgMOG.apply(webcam_image, mask);
					
					for(int i = 0; i < 3; i++){
						CoreModule.dilating(mask, 0, 3, mask);

						List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
						Imgproc.findContours(mask, contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
						Imgproc.drawContours(mask, contours, -1, new Scalar(255, 255, 255),-1);

						//CoreModule.eroding(mask, 0, 3, mask);
					}

					Mat sinFondo = new Mat();
					Imgproc.cvtColor(mask, mask, Imgproc.COLOR_GRAY2RGB);
					Core.bitwise_and(webcam_image, mask, sinFondo);
					
					//CoreModule.writeMatToFile(sinFondo, "sinFondo.jpg");
					ArrayList<String> cards=new ArrayList<String>();
					cards.add("atog.jpg");cards.add("lithatog.jpg");cards.add("sarcatog.jpg");cards.add("thaumatog.jpg");
					for (int c=0;c<4;c++){
						Mat src1 = CoreModule.readMatFromImageFile(cards.get(c));
						//Mat src2 = CoreModule.readMatFromImageFile("carta2.jpg");
						Mat dst = new Mat();
						if(framesCounter % maximumFramesToBeChecked == 0)
						{
							DBSCANClusterer<DoublePoint> dbscanOfCentroids = new DBSCANClusterer<DoublePoint>(dbscanRadius, dbscanMinCentroids);
							ArrayList<Cluster<DoublePoint>> generatedClustersFromCentroids = (ArrayList<Cluster<DoublePoint>>) dbscanOfCentroids.cluster(currentCentroidsFromClusters);
							System.err.println("Numero de clusters: " + generatedClustersFromCentroids.size());
							double sumX2 = 0.0;
							double sumY2 = 0.0;
							double meanX2, meanY2;
							if(generatedClustersFromCentroids.size() > 0)
							{
								for (int k = 0; k < generatedClustersFromCentroids.size(); k++) {
									ArrayList<DoublePoint> listOfDoublePoints = (ArrayList<DoublePoint>) generatedClustersFromCentroids.get(k).getPoints();
									for (int i = 0; i < listOfDoublePoints.size(); i++) {
										double[] current = listOfDoublePoints.get(i).getPoint();
	
										sumX2 += current[0];
										sumY2 += current[1];	
										
										Core.circle(sinFondo, new Point(current[0], current[1]), 2, new Scalar(255, 0, 0),-1);
									}
									meanX2 = sumX2 /  listOfDoublePoints.size();
									meanY2 = sumY2 /  listOfDoublePoints.size();
									if (c==0){
										Core.circle(sinFondo, new Point(meanX2, meanY2), 10, new Scalar(0, 0, 255), -1);
										Core.putText(sinFondo, "Atog", new Point(meanX2, meanY2), Core.FONT_HERSHEY_COMPLEX, 1, new Scalar(0, 0, 255));
									}else if(c==1){
										Core.circle(sinFondo, new Point(meanX2, meanY2), 10, new Scalar(0, 255, 0), -1);
										Core.putText(sinFondo, "Lithatog", new Point(meanX2, meanY2), Core.FONT_HERSHEY_COMPLEX, 1, new Scalar(0, 255, 0));
									}else if(c==2){
										Core.circle(sinFondo, new Point(meanX2, meanY2), 10, new Scalar(255, 0, 0), -1);
										Core.putText(sinFondo, "Sarcatog", new Point(meanX2, meanY2), Core.FONT_HERSHEY_COMPLEX, 1, new Scalar(255, 0, 0));
									}else{
										Core.circle(sinFondo, new Point(meanX2, meanY2), 10, new Scalar(255, 255, 255), -1);
										Core.putText(sinFondo, "Thaumatog", new Point(meanX2, meanY2), Core.FONT_HERSHEY_COMPLEX, 1, new Scalar(255, 255, 255));
									}
									
								}
							}
							
							framesCounter = 1;
							currentCentroidsFromClusters = CentroideSURF_DistanciaMejoradoClustering.surf(src1, sinFondo, dst, dbScanPerFrameRadius, dbScanPerFrameMinPoints );
						}
						else
						{
							framesCounter++;
							currentCentroidsFromClusters.addAll(CentroideSURF_DistanciaMejoradoClustering.surf(src1, sinFondo, dst,dbScanPerFrameRadius, dbScanPerFrameMinPoints));
						}	
						System.out.println("Centroids on frame " + framesCounter + " = " + currentCentroidsFromClusters.toString());
						temp = matToBufferedImage(sinFondo);
						panel.setimage(temp);
						panel.repaint();
					}
				} else {
					break;
				}
			}
		} else {
			System.err.println("Camara no detectada");
		}
		return;
	}

	public void mouseClicked(MouseEvent e) {
		inicio = true;
	}

	public void mousePressed(MouseEvent e) {

	}

	public void mouseReleased(MouseEvent e) {

	}

	public void mouseEntered(MouseEvent e) {

	}

	public void mouseExited(MouseEvent e) {

	}

	public void run() {
		main(null);
	}
}
